package jxtend.processor.utils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import jxtend.processor.exception.JxtendReflectionException;

public final class ReflectUtils {

	public static Method getPublicMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
		try {
			return clazz.getMethod(name, parameterTypes);
		} catch (SecurityException e) {
			throw new JxtendReflectionException(e);
		} catch (NoSuchMethodException e) {
			throw new JxtendReflectionException(e);
		}
	}

	public static Method getInaccessibleMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
		try {
			 Method result = clazz.getDeclaredMethod(name, parameterTypes);
			 result.setAccessible(true);
			 return result;
		} catch (SecurityException e) {
			throw new JxtendReflectionException(e);
		} catch (NoSuchMethodException e) {
			throw new JxtendReflectionException(e);
		}
	}	
	
	public static Object invokeMethod(Method method, Object obj, Object... args) {
		try {
			return method.invoke(obj, args);
		} catch (IllegalArgumentException e) {
			throw new JxtendReflectionException(e);
		} catch (IllegalAccessException e) {
			throw new JxtendReflectionException(e);
		} catch (InvocationTargetException e) {
			throw new JxtendReflectionException(e);
		}
	}
	public static <T> Constructor<T> getInaccessibleConstructor(Class<T> clazz, Class<?>... parameterTypes) {
		try {
			Constructor<T> result = clazz.getDeclaredConstructor(parameterTypes);
			result.setAccessible(true);

			return result;
		} catch (SecurityException e) {
			throw new JxtendReflectionException(e);
		} catch (NoSuchMethodException e) {
			throw new JxtendReflectionException(e);
		}
	}	

	public static <T> Constructor<T> getPublicConstructor(Class<T> clazz, Class<?>... parameterTypes) {
		try {
			Constructor<T> result = clazz.getConstructor(parameterTypes);

			return result;
		} catch (SecurityException e) {
			throw new JxtendReflectionException(e);
		} catch (NoSuchMethodException e) {
			throw new JxtendReflectionException(e);
		}
	}

	public static <T> T newInstance(Constructor<T> constructor, Object... initargs) {
		try {
			return constructor.newInstance(initargs);
		} catch (SecurityException e) {
			throw new JxtendReflectionException(e);
		} catch (IllegalArgumentException e) {
			throw new JxtendReflectionException(e);
		} catch (IllegalAccessException e) {
			throw new JxtendReflectionException(e);
		} catch (InstantiationException e) {
			throw new JxtendReflectionException(e);
		} catch (InvocationTargetException e) {
			throw new JxtendReflectionException(e);
		}
	}	

	public static Object inaccessibleFieldValue(Object obj, String fieldName) {
		Field field;

		try {
			field = obj.getClass().getDeclaredField(fieldName);
			field.setAccessible(true);
		} catch (NoSuchFieldException e) {
			throw new JxtendReflectionException(e);
		}

		return fieldValue(field, obj);
	}

	public static void setInaccessibleFieldValue(Object obj, String fieldName, Object fieldValue) {
		Field field = getInaccessibleField(obj.getClass(), fieldName);
		setFieldValue(field, obj, fieldValue);
	}

	public static void setFieldValue(Field field, Object obj, Object fieldValue) {
		try {
			field.set(obj, fieldValue);
		} catch (IllegalArgumentException e) {
			throw new JxtendReflectionException(e);
		} catch (IllegalAccessException e) {
			throw new JxtendReflectionException(e);
		}
	}

	
	
	public static <T> Field getInaccessibleField(Class<T> clazz, String fieldName) {
		try {
			Field result = clazz.getDeclaredField(fieldName);
			result.setAccessible(true);

			return result;
		} catch (SecurityException e) {
			throw new JxtendReflectionException(e);
		} catch (NoSuchFieldException e) {
			throw new JxtendReflectionException(e);
		}
	}

	public static <T> Field findInaccessibleField(Class<T> clazz, String fieldName) {
		try {
			Field result = clazz.getDeclaredField(fieldName);
			result.setAccessible(true);

			return result;
		} catch (SecurityException e) {
			throw new JxtendReflectionException(e);
		} catch (NoSuchFieldException e) {
			return null;
		}
	}

	public static Field getPublicField(Class<?> clazz, String name) {
		try {
			return clazz.getField(name);
		} catch (SecurityException e) {
			throw new JxtendReflectionException(e);
		} catch (NoSuchFieldException e) {
			throw new JxtendReflectionException(e);
		}
	}		

	public static Field findPublicField(Class<?> clazz, String name) {
		try {
			return clazz.getField(name);
		} catch (SecurityException e) {
			throw new JxtendReflectionException(e);
		} catch (NoSuchFieldException e) {
			return null;
		}
	}	

	public static Object fieldValue(Field field, Object obj) {
		try {
			return field.get(obj);
		} catch (SecurityException e) {
			throw new JxtendReflectionException(e);
		} catch (IllegalArgumentException e) {
			throw new JxtendReflectionException(e);
		} catch (IllegalAccessException e) {
			throw new JxtendReflectionException(e);
		}		
	}

	public static Object invokeInaccessibleNoParameterMethod(Object bean, String methodName) {
		try {
			Method method = bean.getClass().getDeclaredMethod(methodName);
			method.setAccessible(true);
			return method.invoke(bean);
		} catch (SecurityException e) {
			throw new JxtendReflectionException(e);
		} catch (NoSuchMethodException e) {
			throw new JxtendReflectionException(e);
		} catch (IllegalArgumentException e) {
			throw new JxtendReflectionException(e);
		} catch (IllegalAccessException e) {
			throw new JxtendReflectionException(e);
		} catch (InvocationTargetException e) {
			throw new JxtendReflectionException(e);
		}
	}

	private ReflectUtils() {
		// utility class
	}
}
